﻿// ********************************************************************
//  文件名: MyAsync.cs
//  描述:
//  作者: 胡宇辰
//  创建时间: 2024-5-9
// 
//  修改历史:
// ********************************************************************

using System.Net;
using System.Net.Sockets;
using System.Text;

namespace MyApp;

public class MyAsync
{
    static Socket serverSocket;
    static SocketAsyncEventArgs acceptArgs = new();
    static SocketAsyncEventArgs receiveArgs = new();
    private static byte[] receiveBuffer = new byte[1024];
    public static void Start()
    {
        acceptArgs.Completed += AcceptEventArg_Completed;
        receiveArgs.Completed += IO_Completed;
        receiveArgs.SetBuffer(receiveBuffer, 0, 1024);
        
        serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        serverSocket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6324));
        serverSocket.Listen(10);
        Console.WriteLine($"Server started， Thread{Thread.CurrentThread.ManagedThreadId}");
        
        StartAccept();
        
        while (true)
        {
            Thread.Sleep(50000);
        }
    }
    
    static void StartAccept()
    {
        bool willRaiseEvent = false;
        while (!willRaiseEvent)
        {
            acceptArgs.AcceptSocket = null;            
            willRaiseEvent = serverSocket.AcceptAsync(acceptArgs);
            if (!willRaiseEvent)
            {
                ProcessAccept(acceptArgs);
            }
        }
    }
    
    static void AcceptEventArg_Completed(object? sender, SocketAsyncEventArgs args) 
    {
        ProcessAccept(args);
        //StartAccept(); 只接收一个
    }
    
    static void ProcessAccept(SocketAsyncEventArgs e)
    {
        Console.WriteLine("Client connection accepted.");
        
        receiveArgs.UserToken = e.AcceptSocket;

        // As soon as the client is connected, post a receive to the connection
        bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(receiveArgs);
        if (!willRaiseEvent)
        {
            ProcessReceive(receiveArgs);
        }
    }
    
    static void IO_Completed(object? sender, SocketAsyncEventArgs e)
    {
        // determine which type of operation just completed and call the associated handler
        switch (e.LastOperation)
        {
            case SocketAsyncOperation.Receive:
                ProcessReceive(e);
                break;
            case SocketAsyncOperation.Send:
                ProcessSend(e);
                break;
            default:
                throw new ArgumentException("The last operation completed on the socket was not a receive or send");
        }
    }
    
    private static void ProcessReceive(SocketAsyncEventArgs e)
    {
        // check if the remote host closed the connection
        if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
        {
            string content = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred);
            Console.WriteLine("The server Receive {0} bytes, content {1}", e.BytesTransferred, content);
            Console.WriteLine($"Receive Success Thread:{Thread.CurrentThread.ManagedThreadId}");
            receiveArgs.SetBuffer(0, e.BytesTransferred);
            Socket socket = (Socket)e.UserToken;
            if (!socket.SendAsync(receiveArgs))
            {
                ProcessSend(receiveArgs);
            }
        }
        else
        {
            CloseClientSocket(e);
        }
    }

    private static void ProcessSend(SocketAsyncEventArgs e)
    {
        Console.WriteLine($" Send End Thread:{Thread.CurrentThread.ManagedThreadId}");
        Socket socket = (Socket)e.UserToken;
        e.SetBuffer(0, 1024);
        bool willRaiseEvent = socket.ReceiveAsync(e);
        if (!willRaiseEvent)
        {
            ProcessReceive(e);
        }
    }
    
    private static void CloseClientSocket(SocketAsyncEventArgs e)
    {
        Socket socket = (Socket)e.UserToken;

        // close the socket associated with the client
        try
        {
            socket.Shutdown(SocketShutdown.Send);
        }
        // throws if client process has already closed
        catch (Exception) { }
        socket.Close();
        Console.WriteLine("A client has been disconnected from the server.");
    }
}